-- judge_core for wTest
-- The MIT License (MIT)
-- Copyright (c) 2016 Yanghe Sun

import sub, gsub, format from string
import unpack from table

rtrim = (str) ->
    str ..= " "
    gsub(str, "(.-)%s", "%1")

split = (str, sep = '\t') ->
    return str if sep == ""
    fields = {}
    pattern = format("([^%s]+)", sep)
    gsub(str, pattern, (c) -> fields[#fields + 1] = c)
    return fields

shorten = (str, size = 12) ->
    return str if #str < size
    size_part = (size - 2) / 2
    return sub(str, 1, size_part) .. ".." .. sub(str, -size_part)

GenericJudge = (std, comparison, separator, comparer) ->
    comparer = comparer or (x, y) -> x == y
    std_tokens = split(std\read("*a"), separator)
    comparison_tokens = split(comparison\read("*a"), separator)

    if #std_tokens ~= #comparison_tokens then
        return { extra:shorten(format("%d tokens Expected,%d tokens Found",
                                      #std_tokens, #comparison_tokens)),
                 ac: false }


    for i = 1, #std_tokens do
        unless comparer(std_tokens[i], comparison_tokens[i]) then
            return { extra: shorten(format("Wrong answer on Token %d,`%s` expected,`%s`found",
                                           i,std_tokens[i], comparison_tokens[i])),
                     ac: false }

    return { extra: shorten(format("OK %d tokens", #std_tokens)), ac: true }

FullTextJudge = (std, comparison, stdin) ->
    trim_comp = (text1, text2) -> rtrim(text1) == rtrim(text2)
    return GenericJudge(std, comparison, "\n", trim_comp)

DoubleJudge = (std, comparison, stdin, epsilon = 1E-3) ->
    double_comp = (text1, text2) -> math.abs(tonumber(text1) - tonumber(text2)) <= epsilon
    return GenericJudge(std, comparison, "\n\t ", double_comp)

TokenJudge = (std, comparison, stdin) -> GenericJudge(std, comparison, "\n\t ")

ByteJudge = (std, comparison, stdin) -> GenericJudge(std, comparison, "")

SpecialJudge = (stdout, comparison, stdin, spjfunc) ->
    local func
    func = load(spjfunc)! if type(spjfunc) == "string"
    func = spjfunc if type(spjfunc) == "function"
    return func(stdout, comparison, stdin)

builtin_judgers = {
    {"byte", "Judge the answer by byte", ByteJudge},
    {"fulltext", "Judge each char", FullTextJudge},
    {"double", "Judge real number", DoubleJudge},
    {"token", "Judge by tokens", TokenJudge},
    {"spj", "Special judge", SpecialJudge}
}

GetJudgeFunc = (name, ...) ->
    argp = { ... }
    for _, v in ipairs(builtin_judgers) do
        if v[1] == name then
            return (stdo, cmp, stdi) -> v[3](stdo, cmp, stdi, unpack(argp))
    return nil

{ :GetJudgeFunc }
